package unipd.esp1112.pro;

import java.util.Calendar;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.ComponentName;
import android.content.ContentValues;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.res.Configuration;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.os.IBinder;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;

/**
 * the application main activity, here the user wait for the GPS signal to be
 * acquired and starts his training activity, then is notified when the limit he
 * has set up earlier is reached ( if he set up one. ). The activity starts a
 * service where the recording actually takes place and just get updated data
 * from it and displays them to the user. Then when the limit is reached or the
 * user wants to stop the recording of his training the collected data are saved
 * to the application database.
 * 
 * @author andrea basso, federico lancerin, alex tomasello
 * 
 *         language : ITALIAN L'idea è la seguente: avvio l'activity, questa va
 *         a prendersi i dati dal database, crea il servizio e glieli passa. Il
 *         servizio riceve i dati dall'activity e, una volta attivato, aggiorna
 *         le misurazioni fino al suo spegnimento. L'activity, ogni secondo,
 *         riceve e inserisce le misurazioni ricevute dal servizio nei textview.
 * 
 */
public class Misurazioni extends Activity {

	/**
	 * variable to store the name of the profile the user is currently using.
	 */
	private String nome;
	/**
	 * string passed from the previous activity that contains the kind of limit
	 * the user want to reach.
	 */
	private String tipoAttivita;
	/**
	 * the actual limit the user has decided to reach.
	 */
	private int limiteImpostato;
	/**
	 * variable used to retrieve an instance of the application private database.
	 */
	private MyOpenHelper op;
	/**
	 * variable used to reference the database.
	 */
	private SQLiteDatabase db;
	/**
	 * intent used to start the recording process of the service.
	 */
	private Intent intentServizi;
	/**
	 * an instance of the system calendar.
	 */
	final Calendar c = Calendar.getInstance();
	/**
	 * Date when the recording process has started.
	 */
	private Date inizioAttivita;
	/**
	 * user data to pass to the service for calories counting.
	 */	
	private boolean isMale; 	// sex
	private int altezza; 		// height
	private int peso; 			// weight
	private int anni; 			// age
	/**
	 * instance of Servizi, the service running and recording data.
	 */
	private Servizi cronoGpsService;
	/**
	 * boolean representing the state of the connection between activity and service.
	 */
	private boolean isBound = false;
	/**
	 * boolean representing if the system GPS service is capable of supplying the number of satellites used for
	 * each location update.
	 */
	private boolean hasNumSatellites;	
	/**
	 * int that represent the step of the recording process so that tasks can behave differently according to the state of this variable
	 * "step" that will assume just these possible values : PREPARAZIONE, PRONTO, REGISTRAZIONE, FINE.
	 */
	private int step = 0;
	private final int PREPARAZIONE = 0;
	private final int PRONTO = 1;
	private final int REGISTRAZIONE = 2;
	private final int FINE = 3;
	/**
	 * timer used to schedule different tasks at different stages of the recording process.
	 */
	private Timer timer;
	/**
	 * button used to start and stop the training session and going beck to main menu.
	 */
	private Button startStop;	
	/**
	 * boolean value, true if the alertdialog that asks the user to enable GPS provider has already been displayed.
	 */
	private boolean alreadyAskedToUser;
	/**
	 * boolean value, true if after the reach of the limit the alertdialog that asks user
	 * if he wishes to stop the recording or continue it has already been displayed.
	 */
	private boolean alreadyAskedToStopOrContinue;
	/**
	 * semaphore used to one-start the next timertask in a timertask sequence.
	 */
	private boolean checkPrePlayAlreadyStarted;
	/**
	 * semaphore used to one-start the next timertask in a timertask sequence.
	 */
	private boolean updateDataFromServiceAlreadyStarted;

	/*
	 * VARIABLES updated with fresh values taken from the service every second.
	 */
	private String datoCronometro;		// clock
	private String datoDistanza;		// distance
	private String datoCalorie;			// calories
	private String datoVelocita;		// medium speed
	private String datoCompletamento;	// total training %
	private String datoVelocitaIst;		// instant speed
	private String datoAltitudine;		// altitude
	private String datoSatelliti;		// # of satellites used for last fix

	/*
	 * TEXTVIEWS updated with updated recording data every second.
	 */
	private TextView cronometro;			// clock
	private TextView distanza;				// distance
	private TextView calorie;				// calories
	private TextView velocita;				// medium speed
	private TextView completamento;			// total training %
	private TextView indiceCompletamento;	// imported to make it not visible in the case of free training ( no limits and so no %)
	private TextView velocitaist;			// instant speed
	private TextView altitudine;			// altitude
	private TextView satelliti;				// # of satellites used for last fix

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.misurazioni);

		inizializzaUI();

		datoCronometro = "0:00:00";
		datoDistanza = "0 m";
		datoCalorie = "0 kcal";
		datoVelocita = "0 km/h";
		datoCompletamento = "0 %";
		datoVelocitaIst = "0 m/s";
		datoAltitudine = "0 m slm";
		datoSatelliti = "0";
		limiteImpostato = 0;
		inizioAttivita = null;

		hasNumSatellites = false;
		alreadyAskedToUser = false;
		alreadyAskedToStopOrContinue = false;
		checkPrePlayAlreadyStarted = false;
		updateDataFromServiceAlreadyStarted = false;

		// creo l'intent che mi servirà a far partire l'aggiornamento del
		// Servizio
		intentServizi = new Intent(this, Servizi.class);
		step = PREPARAZIONE;

		// ricevo il nome utente dall'intent con cui è stata lanciata
		// l'activity
		Intent intent = getIntent();
		nome = intent.getStringExtra("profilo");
		tipoAttivita = intent.getStringExtra("attivita");
		if (!(tipoAttivita.equalsIgnoreCase("libero"))) {
			limiteImpostato = Integer.parseInt(intent.getStringExtra("limite"));
		} else {
			completamento.setVisibility(View.INVISIBLE);
			indiceCompletamento.setVisibility(View.INVISIBLE);
		}

		// apro database e raccolgo dati profilo da passare al servizio
		op = new MyOpenHelper(this);
		db = op.getReadableDatabase();
		Cursor query = db.rawQuery("SELECT * FROM " + op.TABLE1 + " where " + op.NOME + "='" + nome + "'", null);
		query.moveToNext();

		// estraggo i dati utente che mi servono dalla query
		altezza = Integer.parseInt(query.getString(3));
		peso = Integer.parseInt(query.getString(4));
		if (query.getString(1).equals("1"))
			isMale = true;
		else
			isMale = false;
		String[] dataSplit = query.getString(2).split("/");
		anni = c.get(Calendar.YEAR) - Integer.parseInt(dataSplit[2]);
		if ((c.get(Calendar.DAY_OF_MONTH) - Integer.parseInt(dataSplit[0])) < 0) {
			if ((c.get(Calendar.MONTH) - 1 - Integer.parseInt(dataSplit[1])) < 0)
				anni--;
		} else {
			if ((c.get(Calendar.MONTH) - Integer.parseInt(dataSplit[1])) < 0)
				anni--;
		}
		db.close();

		timer = new Timer();
		// timer.schedule(checkPrePlay, 1000, 2000);
		timer.schedule(new TimerTask() {
			@Override
			public void run() {
				runOnUiThread(checkGpsProvider);
			}
		}, 1000, 15000);

		// assegno l'azione da compiere al bottone start/stop/home
		startStop.setOnClickListener(startStopClick);
		startStop.setClickable(false);
	}

	@Override
	public void onConfigurationChanged(Configuration nuovaConf) {
		super.onConfigurationChanged(nuovaConf);
		setContentView(R.layout.misurazioni);
		inizializzaUI();
		if (tipoAttivita.equalsIgnoreCase("libero")) {
			completamento.setVisibility(View.INVISIBLE);
			indiceCompletamento.setVisibility(View.INVISIBLE);
		}
		AggiornaDatiUi();
		startStop.setOnClickListener(startStopClick);
		startStop.setClickable(false);
		switch (step) {
		case (PRONTO): {
			startStop.setBackgroundResource(R.drawable.play);
			startStop.setClickable(true);
			break;
		}
		case (REGISTRAZIONE): {
			startStop.setBackgroundResource(R.drawable.stop);
			startStop.setClickable(true);
			break;
		}
		case (FINE): {
			startStop.setBackgroundResource(R.drawable.home);
			startStop.setClickable(true);
			break;
		}
		default:
			break;
		}
	}

	@Override
	protected void onPause() {
		super.onPause();
	}

	@Override
	protected void onResume() {
		doBindService();
		super.onResume();
	}

	@Override
	protected void onDestroy() {
		super.onDestroy();
		doUnbindService();
		stopService(intentServizi);
		timer.cancel();
	}
	
	/**
	 * variable to start the connection between this activity and the service.
	 */	
	private ServiceConnection connessione = new ServiceConnection() {
		@Override
		public void onServiceConnected(ComponentName className, IBinder service) {
			cronoGpsService = ((Servizi.ServiziBinder) service).getService();
		}

		@Override
		public void onServiceDisconnected(ComponentName className) {
			cronoGpsService = null;
		}
	};

	/**
	 * method to bind activity and service.
	 */
	void doBindService() {
		bindService(new Intent(this, Servizi.class), connessione, this.BIND_AUTO_CREATE);
		System.out.println("doBind called");
		isBound = true;
	}

	/**
	 * method to unbind activity and service.
	 */
	void doUnbindService() {
		if (isBound) {
			unbindService(connessione);
			System.out.println("doUnbind called");
			isBound = false;
		}
	}

	/**
	 * timertask that runs through all the recording process checking avery 15 seconds if the GPS provider is enabled.
	 */
	TimerTask checkGpsProvider = new TimerTask() {
		@Override
		public void run() {
			if (!cronoGpsService.isProviderEnabled() && !alreadyAskedToUser) {
				alreadyAskedToUser = true;
				showGPSDisabledAlertToUser();
			}
			if (!checkPrePlayAlreadyStarted && cronoGpsService.isProviderEnabled()) {
				timer.schedule(new TimerTask() {
					@Override
					public void run() {
						runOnUiThread(checkPrePlay);
					}
				}, 0, 1000);
				checkPrePlayAlreadyStarted = true;
			}
		}
	};

	/**
	 * timertask that when the GPS provider is enabled check the service to know if the first GPS fix has arrived
	 * and then enables the "startStop" button.
	 */
	TimerTask checkPrePlay = new TimerTask() {
		@Override
		public void run() {
			if (!updateDataFromServiceAlreadyStarted) {
				System.out.println("checking gps status..");
				if (!updateDataFromServiceAlreadyStarted && cronoGpsService.isProviderEnabled() && cronoGpsService.checkFix()) {
					startStop.setClickable(true);
					startStop.setBackgroundResource(R.drawable.play);
					step = 1;
					System.out.println("GPS signal acquired, ready to start");
					timer.schedule(new TimerTask() {
						@Override
						public void run() {
							runOnUiThread(updateDataFromService);
						}
					}, 1000, 1000);
					updateDataFromServiceAlreadyStarted = true;
				}
			}
		}
	};

	/**
	 * timertask that updates activity data fetching updated data from the service.
	 */
	TimerTask updateDataFromService = new TimerTask() {
		@Override
		public void run() {
			datoVelocitaIst = cronoGpsService.getIstSpeed() + " m/s";
			datoAltitudine = "" + cronoGpsService.getAltitude() + " m";
			datoSatelliti = "" + cronoGpsService.getNumSatellites();
			if (step == REGISTRAZIONE) {
				datoCronometro = cronoGpsService.getTime();
				datoDistanza = cronoGpsService.getDistance() + " m";
				datoCalorie = cronoGpsService.getCalories() + " kcal";
				datoVelocita = cronoGpsService.getMedSpeed() + " km/h";
				if (!(tipoAttivita.equalsIgnoreCase("libero")))
					datoCompletamento = cronoGpsService.getPercent() + " %";
				if ((!alreadyAskedToStopOrContinue) && cronoGpsService.hasLimitBeenReached()) {
					alreadyAskedToStopOrContinue = true;
					askUserToStopOrContinue();
				}
			}
			checkNumSatellites();
			AggiornaDatiUi();
			if (step > REGISTRAZIONE)
				this.cancel();
		}
	};

	/**
	 * method called to initialize the UI on creation and on configuration changed.
	 */
	private void inizializzaUI() {
		// importo le view che dovrò aggiornare
		cronometro = (TextView) findViewById(R.id.cronometro);
		distanza = (TextView) findViewById(R.id.metri);
		calorie = (TextView) findViewById(R.id.calorie);
		velocita = (TextView) findViewById(R.id.velocita);
		completamento = (TextView) findViewById(R.id.percentualecompletamento);
		indiceCompletamento = (TextView) findViewById(R.id.completamento);
		velocitaist = (TextView) findViewById(R.id.velocitaistant);
		altitudine = (TextView) findViewById(R.id.altitudine);
		satelliti = (TextView) findViewById(R.id.satelliti);
		startStop = (Button) findViewById(R.id.startStop);
	}

	/**
	 * method that updates the UI every time it is called.
	 */
	private void AggiornaDatiUi() {
		cronometro.setText(datoCronometro);
		distanza.setText(datoDistanza);
		calorie.setText(datoCalorie);
		velocita.setText(datoVelocita);
		if (!(tipoAttivita.equalsIgnoreCase("libero")))
			completamento.setText(datoCompletamento);
		velocitaist.setText(datoVelocitaIst);
		altitudine.setText(datoAltitudine);
		if (hasNumSatellites)
			satelliti.setText(datoSatelliti);
		else if (!hasNumSatellites)
			satelliti.setText("Non Disponibile");
		else
			satelliti.setText("0");
	}

	/**
	 * method to display an alertdialog asking user to enable GPS provider.
	 */
	private void showGPSDisabledAlertToUser() {
		AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this);
		alertDialogBuilder.setMessage("Il GPS è disabilitato, per il corretto funzionamento dell'applicazione dev'essere abilitato, vuoi abilitarlo? ").setCancelable(false)
				.setPositiveButton("Vai alle Impostazioni", new DialogInterface.OnClickListener() {
					public void onClick(DialogInterface dialog, int id) {
						Intent callGPSSettingIntent = new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS);
						startActivity(callGPSSettingIntent);
						alreadyAskedToUser = false;
						dialog.cancel();
					}
				});
		alertDialogBuilder.setNegativeButton("Annulla", new DialogInterface.OnClickListener() {
			public void onClick(DialogInterface dialog, int id) {
				alreadyAskedToUser = false;
				doUnbindService();
				getApplicationContext().stopService(intentServizi);
				SelezioneAttivita.daTerminare = true;
				finish();
				dialog.cancel();
			}
		});
		AlertDialog alertGpsDisabled = alertDialogBuilder.create();
		alertGpsDisabled.show();
	}

	/**
	 * method to display an alertdialog asking the user if he wants to continue the recording process or to 
	 * stop it. This method is called when the limit is reached, if there is one.
	 */
	private void askUserToStopOrContinue() {
		AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this);
		alertDialogBuilder.setMessage("Il limite impostato è stato raggiunto, vuoi terminare qui l'attività o vuoi continuare la registrazione?");
		alertDialogBuilder.setCancelable(false);
		alertDialogBuilder.setPositiveButton("Termina", new DialogInterface.OnClickListener() {
			@Override
			public void onClick(DialogInterface dialog, int which) {
				System.out.println("Service is going to be killed");
				cronoGpsService.disableWarning();
				doUnbindService();
				getApplicationContext().stopService(intentServizi);
				startStop.setBackgroundResource(R.drawable.home);
				step = 3;
				db = op.getWritableDatabase();
				ContentValues values = new ContentValues();
				values.put(op.NOME, nome);
				// data e ora di inizio registrazione
				if (inizioAttivita.getMinutes() < 10) {
					values.put(op.DATAEORA, "" + inizioAttivita.getDate() + "/" + (inizioAttivita.getMonth() + 1) + "/" + (inizioAttivita.getYear() + 1900) + "-" + inizioAttivita.getHours() + ":0"
							+ inizioAttivita.getMinutes());
				} else
					values.put(op.DATAEORA, "" + inizioAttivita.getDate() + "/" + (inizioAttivita.getMonth() + 1) + "/" + (inizioAttivita.getYear() + 1900) + "-" + inizioAttivita.getHours() + ":"
							+ inizioAttivita.getMinutes());
				// tipo allenamento
				values.put(op.TIPO, tipoAttivita);
				// distanza percorsa
				values.put(op.DISTANZA, datoDistanza);
				// tempo totale
				values.put(op.DURATA, datoCronometro);
				// calorie consumate
				values.put(op.CALORIE, datoCalorie);
				// velocità media
				values.put(op.VELOCITA, datoVelocita);
				// inserisco i dati nel DB
				db.insertOrThrow(op.TABLE2, null, values);
				db.close();
				dialog.cancel();
			}
		});
		alertDialogBuilder.setNegativeButton("Continua", new DialogInterface.OnClickListener() {
			@Override
			public void onClick(DialogInterface dialog, int which) {
				cronoGpsService.disableWarning();
				dialog.cancel();
			}
		});
		AlertDialog alertStopContinue = alertDialogBuilder.create();
		alertStopContinue.show();
	}

	/**
	 * method to check if GPS proider can provide the number of satellites used for each location fix.
	 */
	private void checkNumSatellites() {
		if (cronoGpsService.getNumSatellites() != 0)
			hasNumSatellites = true;
		else
			hasNumSatellites = false;
	}

	/**
	 * onclicklistener of the button startStop
	 */
	private OnClickListener startStopClick = new OnClickListener() {
		@Override
		public void onClick(View v) {
			// partenza cronometro allo start
			if (step == PRONTO) {
				cronoGpsService.setProfileData(isMale, anni, altezza, peso);
				cronoGpsService.setTrainingParameters(tipoAttivita, limiteImpostato);
				getApplicationContext().startService(intentServizi);
				inizioAttivita = c.getTime();
				startStop.setBackgroundResource(R.drawable.stop);
				step = 2;
				System.out.println("Receiving updates");
			}
			// stop aggiornamenti e cronometro quando premo stop
			else if (step == REGISTRAZIONE) {
				System.out.println("Service is going to be killed");
				doUnbindService();
				getApplicationContext().stopService(intentServizi);
				startStop.setBackgroundResource(R.drawable.home);
				step = 3;
				// apro database e inserisco i dati della corsa
				db = op.getWritableDatabase();
				ContentValues values = new ContentValues();
				values.put(op.NOME, nome);
				// data e ora di inizio registrazione
				if (inizioAttivita.getMinutes() < 10) {
					values.put(op.DATAEORA, "" + inizioAttivita.getDate() + "/" + (inizioAttivita.getMonth() + 1) + "/" + (inizioAttivita.getYear() + 1900) + "-" + inizioAttivita.getHours() + ":0"
							+ inizioAttivita.getMinutes());
				} else
					values.put(op.DATAEORA, "" + inizioAttivita.getDate() + "/" + (inizioAttivita.getMonth() + 1) + "/" + (inizioAttivita.getYear() + 1900) + "-" + inizioAttivita.getHours() + ":"
							+ inizioAttivita.getMinutes());
				// tipo allenamento
				values.put(op.TIPO, tipoAttivita);
				// distanza percorsa
				values.put(op.DISTANZA, datoDistanza);
				// tempo totale
				values.put(op.DURATA, datoCronometro);
				// calorie consumate
				values.put(op.CALORIE, datoCalorie);
				// velocità media
				values.put(op.VELOCITA, datoVelocita);
				// inserisco i dati nel DB
				db.insertOrThrow(op.TABLE2, null, values);
				db.close();
			} else if (step == FINE) {
				SelezioneAttivita.daTerminare = true;
				finish();
			}
		}
	};

	
}
